﻿' ----------------------------------------------------------------------
'
' DirectoryInfo.vb
'
'   Dbfs.Domain
'
'   Exposes instance methods for creating, moving, and enumerating through 
'   directories and subdirectories. This class cannot be inherited.
'
'   Use the DirectoryInfo class for typical operations such as copying, 
'   moving, renaming, creating, and deleting directories.
'
' ----------------------------------------------------------------------
' version:  1.0.0.1
' today:    09/07/09  12:45:00
' ident:    "@(#)/Dbfs/Domain/DirectoryInfo.vb 1.0 0.1 09/07/09 0 rjs"

Imports System.IO
Imports System.Security.AccessControl
Imports System.Text.RegularExpressions
Imports Dbfs.Data
Imports Dbfs.Security


Namespace Dbfs

    ''' <summary>
    ''' (DBFS) Exposes instance methods for creating, moving, and enumerating through 
    ''' directories and subdirectories. This class cannot be inherited.
    ''' </summary>
    ''' <remarks>Use the DirectoryInfo class for typical operations such as 
    ''' copying, moving, renaming, creating, and deleting directories.</remarks>
    <SerializableAttribute()> _
    Public Class DirectoryInfo
        Inherits FileSystemInfo
        Implements IDbfsDirectoryNode

        ''' <summary>
        ''' A collection of child <see cref="FileSystemInfo"/>.
        ''' </summary>
        ''' <remarks></remarks>
        Private _children As NodeCollection

        ''' <summary>
        ''' A list of <see cref="DirectoryInfo"/> that have been displaced by
        ''' directories that have been mounted in their place.
        ''' </summary>
        ''' <remarks></remarks>
        Private _mountPoints As NodeCollection

        ''' <summary>
        ''' The <i>root</i> directory object.
        ''' </summary>
        ''' <remarks></remarks>
        Private _root As DirectoryInfo


#Region " Constructors "
        Protected Sub New()
            MyBase.New()
        End Sub

        ''' <summary>
        ''' Initializes a new instance of the <see cref="DirectoryInfo"/> class.
        ''' </summary>
        ''' <param name="path">A string specifying the path on which to create the <b>DirectoryInfo</b>.</param>
        ''' <remarks>If the path is fully qualified with a UNC name, a folder is created that
        ''' can be persisted in that filesystem.  If the path does not contain a UNC name,
        ''' a folder is created within the virtual space for the current user.</remarks>
        Public Sub New(ByVal path As String)
            MyBase.New(path)
            Entity = Context.Read(Of DirectoryEntity)(path)
        End Sub
#End Region ' Constructors

#Region " Properties "
        ''' <summary>
        ''' Gets the collection of child elements on the object.
        ''' </summary>
        ''' <value></value>
        ''' <returns>The collection of child FileSystemInfo objects.</returns>
        ''' <remarks>This property is read-only.</remarks>
        Protected Friend ReadOnly Property Children() As NodeCollection Implements IDbfsDirectoryNode.Children
            Get
                If _children Is Nothing Then
                    _children = New NodeCollection
                    _children.Append(CType(Entity, DirectoryEntity).GetFileSystemInfos())
                    For Each item In _children
                        item.ParentInfo = Me
                    Next
                End If
                Return _children
            End Get
        End Property

        ''' <summary>
        ''' Indicates whether the hierarchical data node that the IHierarchyData 
        ''' object represents has any child nodes.
        ''' </summary>
        ''' <value><b>true</b> if the current node has child nodes; otherwise, <b>false</b>.</value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property HasChildren() As Boolean Implements IDbfsDirectoryNode.HasChildren
            Get
                ' can't simple do a Children.Count since that would trigger a loading
                ' of the children
                If _children IsNot Nothing AndAlso _children.Count > 0 Then Return True
                Return CType(Entity, DirectoryEntity).HasChildren
            End Get
        End Property

        ''' <summary>
        ''' Gets the name of this folder.
        ''' </summary>
        ''' <value>A String containing the name for this folder.</value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Overrides ReadOnly Property Name() As String
            Get
                Return Entity.Name
            End Get
        End Property

        ''' <summary>
        ''' Gets the parent directory of a specified subdirectory.
        ''' </summary>
        ''' <value>The parent directory, or a null reference (<b>Nothing</b> in Visual Basic) 
        ''' if the path is null or if the file path denotes a root 
        ''' (such as "\", "C:", or * "\\server\share").</value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Overridable ReadOnly Property Parent() As DirectoryInfo
            Get
                Return ParentInfo
            End Get
        End Property

        ''' <summary>
        ''' Gets the root portion of a path.
        ''' </summary>
        ''' <value>A <see cref="DirectoryInfo"/> object representing the root of a path.</value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property Root() As DirectoryInfo
            Get
                Return _root
            End Get
        End Property

        ''' <summary>
        ''' Gets the short 8.3 version of the name for this folder.
        ''' </summary>
        ''' <value></value>
        ''' <returns>A String containing the 8.3 name for this folder.</returns>
        ''' <remarks></remarks>
        Public Overrides ReadOnly Property ShortName() As String 'Implements IDbfsNodeItem.ShortName
            Get
                Return Entity.ShortName
            End Get
        End Property
#End Region ' Properties

#Region " Methods "
        ''' <summary>
        ''' Creates a directory.
        ''' </summary>
        ''' <remarks>If the directory already exists, this method does nothing.</remarks>
        Public Overridable Sub Create()

            ' local variables
            Dim security As DbfsDirectorySecurity

            ' if the directory already exists, this method does nothing
            If Exists Then Return

            ' a root directory will always be marked as Exists since we never write
            ' them to the database, so at this point we must have some child directory

            ' apply security, check parent to see if we can create the directory
            If (Parent.AccessRights And FileSystemRights.CreateDirectories) = 0 Then
                Throw New DbfsException("Access Denied")
            End If

            ' if this item is new and if the parent is also new, the parent must
            ' be created first so that our parent node id is correct
            Parent.Create()

            ' this item must be added as a child of the parent
            Parent.Children.Add(Me)
            Parent.Children.Sort()

            ' add this item to the database
            Context.Create(Entity)

            ' we have to update security to get the inherited permissions
            security = Entity.FileSystemSecurity

        End Sub

        ''' <summary>
        ''' Creates a subdirectory or subdirectories on the specified path. The specified path can be 
        ''' relative to this instance of the <see cref="DirectoryInfo"/> class.
        ''' </summary>
        ''' <param name="path">The specified path. This cannot be a different disk volume 
        ''' or Universal Naming Convention (UNC) name.</param>
        ''' <returns>The last object specified in path.</returns>
        ''' <remarks>The subdirectory being created is not guaranteed to be an
        ''' immediate child of the current directory, so the parent of the returned
        ''' DirectoryInfo object is not immediately defined.</remarks>
        Public Overridable Function CreateSubdirectory(ByVal path As String) As DirectoryInfo

            ' apply security
            If (AccessRights And FileSystemRights.CreateDirectories) = 0 Then
                Throw New DbfsException("Access Denied")
            End If

            ' resolve any relative pathing
            path = Directory.ResolvePath(OriginalPath, path)

            Dim di As DirectoryInfo = New DirectoryInfo(path)
            If di.Exists Then
                Throw New IOException("The specified directory already exists.")
            End If

            di.Create()

            ' return a DirectoryInfo for the new directory
            Return di

        End Function

        ''' <summary>
        ''' Creates a subdirectory of the current directory with a unique name.
        ''' </summary>
        ''' <returns>The new directory.</returns>
        ''' <remarks></remarks>
        Public Function CreateNewSubDirectory() As DirectoryInfo

            Dim baseName As String = FullName & DIRECTORY_SEP_CHAR
            Dim i As Integer
            Dim min As Integer
            Dim szName As String

            ' initialize variables
            min = 1

            ' find a unique new name
            If HasChildren Then
                For Each child As FileSystemInfo In Children
                    szName = child.Name.ToLower
                    If Left(szName, 12) = "new folder #" Then
                        i = szName.Substring(12)
                        If i >= min Then
                            min = i + 1
                        End If
                    End If
                Next
            End If

            Return CreateSubdirectory(baseName & "New Folder #" & min)

        End Function

        ''' <summary>
        ''' Deletes this <see cref="DirectoryInfo"/> if it is empty.
        ''' </summary>
        ''' <remarks></remarks>
        Public Overrides Sub Delete()
            Delete(False)
        End Sub

        ''' <summary>
        ''' Deletes this <see cref="DirectoryInfo"></see>, specifying whether
        ''' to delete subdirectories and files.
        ''' </summary>
        ''' <remarks></remarks>
        Public Overloads Sub Delete(ByVal recursive As Boolean)

            ' throw an exception if the folder is not empty
            If recursive = False And HasChildren Then
                Throw New IOException("The folder is not empty")
            End If

            ' apply security
            If (Parent.AccessRights And FileSystemRights.DeleteSubdirectoriesAndFiles) = 0 OrElse _
               (AccessRights And FileSystemRights.Delete) = 0 Then
                Throw New DbfsException("Access Denied")
            End If

            ' if there are any children, delete them first
            For i As Int16 = Children.Count - 1 To 0 Step -1
                Dim item As FileSystemInfo = Children(i)
                ' children marked as directories are deleted recursively
                If (item.Attributes And FileAttributes.Directory) = FileAttributes.Directory Then
                    CType(item, DirectoryInfo).Delete(True)
                Else
                    item.Delete()
                End If
                Children.RemoveAt(i)
            Next

            Context.Delete(Entity)

        End Sub

        ''' <summary>
        ''' Gets a <see cref="DirectorySecurity"/> object that encapsulates the 
        ''' access control list (ACL) entries for the directory described by the 
        ''' current <see cref="DirectoryInfo"/> object.
        ''' </summary>
        ''' <returns>A <see cref="DirectorySecurity"/> object that encapsulates 
        ''' the access control rules for the directory.</returns>
        ''' <remarks></remarks>
        Public Overrides Function GetAccessControl() As Security.FileSystemSecurity
            ' apply security, check to see if we can read the access control list
            If (AccessRights And FileSystemRights.ReadPermissions) = 0 Then
                Throw New DbfsException("Access Denied")
            End If
            If Entity.FileSystemSecurity Is Nothing Then
                Throw New SystemException("Invalid security")
            End If
            Return Entity.FileSystemSecurity
        End Function

        ''' <summary>
        ''' Returns the subdirectories of the current directory.
        ''' </summary>
        ''' <returns>An array of <see cref="DirectoryInfo"/> objects.</returns>
        ''' <remarks></remarks>
        Public Overridable Function GetDirectories() As DirectoryInfo()
            Return GetDirectories("*", SearchOption.TopDirectoryOnly)
        End Function

        ''' <summary>
        ''' Returns an array of directories in the current DirectoryInfo 
        ''' matching the given search criteria.
        ''' </summary>
        ''' <param name="searchPattern">The search string, such as "System*", 
        ''' used to search for all directories beginning with the word "System".</param>
        ''' <returns>An array of type DirectoryInfo matching searchPattern.</returns>
        ''' <remarks></remarks>
        Public Function GetDirectories(ByVal searchPattern As String) As DirectoryInfo()
            Return GetDirectories(searchPattern, SearchOption.TopDirectoryOnly)
        End Function

        ''' <summary>
        ''' Returns an array of directories in the current DirectoryInfo 
        ''' matching the given search criteria 
        ''' and using a value to determine whether to search subdirectories.
        ''' </summary>
        ''' <param name="searchPattern">The search string, such as "System*", 
        ''' used to search for all directories beginning with the word "System".</param>
        ''' <param name="searchOption">One of the values of the SearchOption enumeration 
        ''' that specifies whether the search operation should include only the current 
        ''' directory or should include all subdirectories.</param>
        ''' <returns>An array of type DirectoryInfo matching searchPattern.</returns>
        ''' <remarks></remarks>
        Public Function GetDirectories(ByVal searchPattern As String, ByVal searchOption As SearchOption) As DirectoryInfo()

            Dim childResults() As DirectoryInfo = Nothing
            Dim masterResults() As DirectoryInfo
            Dim pattern As String
            Dim rx As Regex = Nothing
            Dim temp() As DirectoryInfo
            Dim top As String

            ' validate parameters
            ValidateSearchArguments(searchPattern, searchOption)

            ' apply security, check to see if we can list the directory
            If (AccessRights And FileSystemRights.ListDirectory) = 0 Then
                Throw New DbfsException("Access Denied")
            End If

            ' split the pattern to get the top-level directory name and the remaining pattern
            ' (N.B. - Cognos names can contain "/")
            Dim i As Int16 = searchPattern.IndexOf("\"c)
            If i = -1 Then
                top = ""
                pattern = searchPattern

                ' the search pattern has to be converted to a regex-type pattern
                Dim regexPattern As String
                If pattern.IndexOfAny("?*") <> -1 Then
                    regexPattern = pattern.Replace("?"c, "."c)
                    regexPattern = regexPattern.Replace("*", ".*")
                    rx = New Regex(regexPattern)
                End If
            Else
                top = searchPattern.Substring(0, i)
                pattern = searchPattern.Substring(i + 1)
            End If

            ' iterate through the list of children and look for any that
            ' match the regular expression given in searchPattern
            Dim m As Int16
            Dim n As Int16
            Dim matches As New ArrayList()

            m = 0   ' accumulated length of child results array, prior to resize
            n = 0   ' accumulated length of child results array, after resize (larger)

            For Each item As FileSystemInfo In Children
                If (item.Attributes And FileAttributes.Directory) Then

                    ' are we matching a mid-path directory or an end-path directory?
                    If i = -1 Then

                        ' end-path directory
                        If rx Is Nothing Then
                            If item.Name = pattern Then
                                matches.Add(item)
                            End If
                        Else
                            If rx.IsMatch(item.Name) Then
                                matches.Add(item)
                            End If
                        End If

                    Else

                        ' mid-path directory
                        If item.Name = top Then
                            temp = CType(item, DirectoryInfo).GetDirectories(pattern, IO.SearchOption.TopDirectoryOnly)
                            If temp.Length > 0 Then
                                n += temp.Length
                                Array.Resize(childResults, n)
                                Array.Copy(temp, 0, childResults, m, temp.Length)
                                m = n
                            End If
                        End If

                    End If

                    ' perform requested recursion
                    If searchOption = IO.SearchOption.AllDirectories Then
                        temp = CType(item, DirectoryInfo).GetDirectories(searchPattern, searchOption)
                        If temp.Length > 0 Then
                            n += temp.Length
                            Array.Resize(childResults, n)
                            Array.Copy(temp, 0, childResults, m, temp.Length)
                            m = n
                        End If
                    End If

                End If
            Next

            n = matches.Count
            masterResults = matches.ToArray(GetType(DirectoryInfo))
            If childResults IsNot Nothing Then
                Array.Resize(masterResults, n + childResults.Length)
                Array.Copy(childResults, 0, masterResults, n, childResults.Length)
            End If

            Return masterResults

        End Function

        ''' <summary>
        ''' Returns an array of files in the current <see cref="DirectoryInfo"/>.
        ''' </summary>
        ''' <returns>An array of type <see cref="FileInfo"/>.</returns>
        ''' <remarks></remarks>
        Public Function GetFiles() As FileInfo()
            Return GetFiles("*", SearchOption.TopDirectoryOnly)
        End Function

        ''' <summary>
        ''' Returns an array of files in the current <see cref="DirectoryInfo"/> 
        ''' matching the given search criteria.
        ''' </summary>
        ''' <param name="searchPattern">The search string, such as "System*", 
        ''' used to search for all directories beginning with the word "System".</param>
        ''' <returns>An array of type <see cref="FileInfo"/> matching <i>searchPattern</i>.</returns>
        ''' <remarks></remarks>
        Public Function GetFiles(ByVal searchPattern As String) As FileInfo()
            Return GetFiles(searchPattern, SearchOption.TopDirectoryOnly)
        End Function

        ''' <summary>
        ''' Returns an array of files in the current <see cref="DirectoryInfo"/>
        ''' matching the given search criteria 
        ''' and using a value to determine whether to search subdirectories.
        ''' </summary>
        ''' <param name="searchPattern">The search string, such as "System*", 
        ''' used to search for all directories beginning with the word "System".</param>
        ''' <param name="searchOption">One of the values of the SearchOption enumeration 
        ''' that specifies whether the search operation should include only the current 
        ''' directory or should include all subdirectories.</param>
        ''' <returns>An array of type <see cref="FileInfo"/> matching searchPattern.</returns>
        ''' <remarks></remarks>
        Public Function GetFiles(ByVal searchPattern As String, ByVal searchOption As SearchOption) As FileInfo()

            Dim childResults() As FileInfo = Nothing
            Dim masterResults() As FileInfo
            Dim pattern As String
            Dim rx As Regex = Nothing
            Dim temp() As FileInfo
            Dim top As String

            ' validate parameters
            ValidateSearchArguments(searchPattern, searchOption)

            ' apply security, check to see if we can list the directory
            If (AccessRights And FileSystemRights.ListDirectory) = 0 Then
                Throw New DbfsException("Access Denied")
            End If

            ' split the pattern to get the top-level directory name and the remaining pattern
            Dim i As Int16 = searchPattern.IndexOfAny(DIRECTORY_SEP_CHARS)
            If i = -1 Then
                top = ""
                pattern = searchPattern

                ' the search pattern has to be converted to a regex-type pattern
                Dim regexPattern As String
                regexPattern = pattern.Replace("?"c, "."c)
                regexPattern = regexPattern.Replace("*", ".*")
                rx = New Regex(regexPattern)
            Else
                top = searchPattern.Substring(0, i)
                pattern = searchPattern.Substring(i + 1)
            End If

            ' iterate through the list of children and look for any that
            ' match the regular expression given in searchPattern
            Dim m As Int16
            Dim n As Int16
            Dim matches As New ArrayList()

            m = 0   ' accumulated length of child results array, prior to resize
            n = 0   ' accumulated length of child results array, after resize (larger)

            For Each item As FileSystemInfo In Children
                If (item.Attributes And FileAttributes.Directory) Then

                    ' perform requested recursion
                    If searchOption = IO.SearchOption.AllDirectories Then
                        temp = CType(item, DirectoryInfo).GetFiles(searchPattern, searchOption)
                        If temp.Length > 0 Then
                            n += temp.Length
                            Array.Resize(childResults, n)
                            Array.Copy(temp, 0, childResults, m, temp.Length)
                            m = n
                        End If
                    End If
                Else
                    ' test for files matching pattern
                    If rx.IsMatch(item.Name) Then
                        matches.Add(item)
                    End If
                End If
            Next

            n = matches.Count
            masterResults = matches.ToArray(GetType(FileInfo))
            If childResults IsNot Nothing Then
                Array.Resize(masterResults, n + childResults.Length)
                Array.Copy(childResults, 0, masterResults, n, childResults.Length)
            End If

            Return masterResults

        End Function

        ''' <summary>
        ''' Returns an array of strongly typed <see cref="FileSystemInfo"></see> 
        ''' entries representing all the child items in an object.
        ''' </summary>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Function GetFileSystemInfos() As FileSystemInfo() Implements IDbfsDirectoryNode.GetFileSystemInfos

            ' local variables
            Dim count As Integer = 0
            Dim fsinfos(0) As FileSystemInfo

            ' apply security, check to see if we can list the directory
            If (AccessRights And FileSystemRights.ListDirectory) = 0 Then
                Throw New DbfsException("Access Denied")
            End If

            Array.Resize(fsinfos, Children.Count)
            For Each fsi As FileSystemInfo In Children
                If Not fsi.Entity.GetType.IsSubclassOf(GetType(FileSystemNonEntity)) Then
                    fsinfos(count) = fsi
                    count += 1
                End If
            Next

            ' minimize the array size
            Array.Resize(fsinfos, count)

            Return fsinfos

        End Function

        ''' <summary>
        ''' Mounts a <see cref="FileSystemInfo"/> object as a child of the current object.
        ''' </summary>
        ''' <param name="directory">The <see cref="DirectoryInfo"/> to be mounted 
        ''' as a child of the current object.</param>
        ''' <remarks>If a child of the same name already exists, it is hidden until the new
        ''' object is removed using <see cref="Unmount"/>.
        ''' <para>The root directory of any filesystem cannot be mounted onto another
        ''' filesystem.  Since the name of the root directory is typically given simply by
        ''' a path separator character, this would cause all sorts of problems when parsing
        ''' the path.  For example, <see cref="GetDirectories"/> will return all children
        ''' if the path ends with a path separator character.
        ''' </para>
        ''' <para>Also note that this is where the <i>Info</i> objects can differ from the
        ''' <i>entity</i>objects in their collections of children, and why the
        ''' <see cref="EffectivePath"/> is a function of <i>Info</i> objects.</para></remarks>
        Public Sub Mount(ByVal directory As FileSystemInfo)
            If directory Is Nothing Then _
                Throw New ArgumentNullException("directory")
            Dim name As String = directory.Name
            If Path.GetFileName(name) = "" Then _
                Throw New ArgumentException("Root directories cannot be mounted")
            If _mountPoints Is Nothing Then _
                _mountPoints = New NodeCollection
            If Children.Contains(name) Then
                _mountPoints.Add(Children(name))
                Children.Remove(name)
            End If
            Children.Add(directory)
            directory.ParentInfo = Me
        End Sub

        ''' <summary>
        ''' Moves a <see cref="DirectoryInfo"/> instance and its contents to a new path.
        ''' </summary>
        ''' <param name="destDirName">The name and path to which to move this directory. 
        ''' The destination cannot be another disk volume or a directory with the identical name. 
        ''' It can be an existing directory to which you want to add this directory as a subdirectory.</param>
        ''' <remarks>This method permits moving a directory to a read-only directory. 
        ''' The read/write attribute of neither directory is affected.</remarks>
        Public Sub MoveTo(ByVal destDirName As String)


            ' temp
            Dim moveObject As DirectoryInfo
            Dim newName As String = destDirName

            Dim targetDir As DirectoryInfo



            ' validate arguments
            If destDirName Is Nothing Then _
                Throw New ArgumentNullException("destDirName")

            If destDirName.Trim.Length = 0 Or destDirName.IndexOfAny(System.IO.Path.GetInvalidPathChars()) <> -1 Then _
                Throw New ArgumentException("destDirName")

            ' enforce source security
            If (AccessRights And FileSystemRights.Delete) = 0 AndAlso _
               (Parent.AccessRights And FileSystemRights.DeleteSubdirectoriesAndFiles) = 0 Then
                Throw New DbfsException("Access Denied")
            End If

            ' resolve any relative pathing
            'destDirName = Directory.ResolvePath(Parent.FullName, destDirName)

            ' see if the destination already exists in the filesystem
            targetDir = New DirectoryInfo(destDirName)

            ' if target does not exist, we move this folder into the parent
            If Not targetDir.Exists Then _
                targetDir = targetDir.Parent

            ' enforce target security
            If (targetDir.AccessRights And FileSystemRights.CreateDirectories) = 0 Then
                Throw New DbfsException("Access Denied")
            End If

            ' check for the simple case of just renaming the current folder
            If Parent.FullPath = targetDir.FullPath Then
                Rename(Path.GetFileName(destDirName))
                Return
            End If




            ' if changes are being tracked, we need to create a new object
            If TrackChanges Then
                Dim t As Date = Now()
                moveObject = Me.Clone
                moveObject.CreationTime = t
                moveObject.LastAccessTime = t
                moveObject.LastWriteTime = t
                Me.Delete()
            Else
                moveObject = Me
                Me.Parent.Children.Remove(Me)
            End If

            targetDir.Children.Add(moveObject)


            ' if changes are being tracked, we need to mark the old record as
            ' invalid and get a new layer of history
            If TrackChanges Then
                ' we'll need a new layer of history in the database
                Dim newHistoryLayer As DirectoryInfo = Me.Clone
                ' this layer represents the 'old' me
                'newHistoryLayer.IsDeleted = True
                ' the 'new' me doesn't exist in the database yet
                'Entity._exists = False
            End If

            'If oldPath <> newPath Then
            ' remove this from the current parent
            If Parent IsNot Nothing Then
                Parent.Children.Remove(Me)
            End If

            'ParentNode = Nothing
            'Entity._parentNodeID = -1
            'End If


        End Sub

        Public Overrides Sub Refresh()
            Entity = Context.Refresh(Of DirectoryEntity)(Entity)
            _children = Nothing
        End Sub

        ''' <summary>
        ''' Applies access control list (ACL) entries described by a <see cref="DirectorySecurity"/> 
        ''' object to the directory described by the current <see cref="DirectoryInfo"/> object.
        ''' </summary>
        ''' <param name="directorySecurity">A <see cref="DirectorySecurity"/> object that describes 
        ''' an ACL entry to apply to this directory.</param>
        ''' <remarks></remarks>
        Public NotOverridable Overrides Sub SetAccessControl(ByVal directorySecurity As System.Security.AccessControl.FileSystemSecurity)
            Dim mySecurity As DbfsDirectorySecurity = New DbfsDirectorySecurity(directorySecurity)
            MyBase.SetAccessControl(mySecurity)
        End Sub

        ''' <summary>
        ''' Returns the original path that was passed by the user.
        ''' </summary>
        ''' <returns>Returns the original path that was passed by the user.</returns>
        ''' <remarks></remarks>
        Public Overrides Function ToString() As String
            Return OriginalPath
        End Function

        ''' <summary>
        ''' Unmounts a child <see cref="FileSystemInfo"/> object from the current object.
        ''' </summary>
        ''' <param name="directory">The <see cref="DirectoryInfo"/> 
        ''' to be from the collection of child objects in the current object.</param>
        ''' <remarks>If the object being removed was previously added using the
        ''' <see cref="Mount"/> method, any hidden object will become visible.</remarks>
        Public Overloads Sub Unmount(ByVal directory As FileSystemInfo)
            If directory Is Nothing Then _
                Throw New ArgumentNullException("directory")
            If Children.Contains(directory.Name) Then
                Children.Remove(directory.Name)
                directory.Entity.Name = Path.GetFileName(directory.FullName)
            End If
            If _mountPoints IsNot Nothing AndAlso _
               _mountPoints.Contains(directory.FullName) Then
                Children.Add(_mountPoints(directory.Name))
                _mountPoints.Remove(directory.Name)
            End If
        End Sub
#End Region ' Methods

#Region " Helper Methods "
        ''' <summary>
        ''' Helper function to perform a simple rename of the current directory.
        ''' </summary>
        ''' <param name="newName">A String specifying the new folder name.</param>
        ''' <remarks></remarks>
        Private Sub Rename(ByVal newName As String)

            ' solve for the easy case first (changes are not being tracked)
            If Not TrackChanges Then
                SetName(newName)
                Return
            End If

            ' if changes are being tracked, we need to mark the old record as
            ' invalid and get a new layer of history

            ' 'delete' the current layer
            Dim utc As Date = Date.UtcNow()

            ' set the new layer of history as the current layer
            'Entity = newHistoryLayer
            CreationTimeUtc = utc
            LastAccessTimeUtc = utc
            'Name = newName

            FullPath = Parent.Name & DIRECTORY_SEP_CHAR & newName
            'OriginalPath = FullPath

        End Sub

        ''' <summary>
        ''' Validates the arguments for the <see cref="GetDirectories"/> and
        ''' <see cref="GetFiles"/> methods.
        ''' </summary>
        ''' <param name="searchPattern">The search string, such as "System*", 
        ''' used to search for all directories beginning with the word "System".</param>
        ''' <param name="searchOption">One of the values of the SearchOption enumeration 
        ''' that specifies whether the search operation should include only the current 
        ''' directory or should include all subdirectories.</param>
        ''' <remarks></remarks>
        Private Sub ValidateSearchArguments(ByVal searchPattern As String, ByVal searchOption As SearchOption)

            ' validate parameters
            If searchPattern Is Nothing Then
                Throw New ArgumentNullException("searchPattern")
            End If

            If searchPattern.IndexOfAny(Path.GetInvalidPathChars()) > 0 Then
                Throw New ArgumentException("Invalid search pattern.")
            End If

            ' validate search option
            If searchOption <> IO.SearchOption.AllDirectories AndAlso _
               searchOption <> IO.SearchOption.TopDirectoryOnly Then _
                Throw New ArgumentOutOfRangeException("Invalid value for search option.")

            ' path cannot begin with a directory separator character, 
            ' the System.IO message for this exceptions is quite obtuse...
            '   "Second path fragment must not be a drive or UNC name."
            '   "Parameter name: path2"
            Dim i As Int16 = searchPattern.IndexOfAny(DIRECTORY_SEP_CHARS)
            If i = 0 Then _
                Throw New ArgumentException("Path cannot begin with a directory separator character.")

            ' wildcards are only allowed in the leaf node of the search path
            Dim j As Int16 = searchPattern.IndexOfAny("?*")
            If j >= 0 AndAlso j < i Then _
                Throw New ArgumentException("Illegal characters in path.")

        End Sub
#End Region ' Helper Methods

    End Class

End Namespace
